package com.chen.sa.auth.controller;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.sign.SaSignUtil;
import cn.dev33.satoken.sso.processor.SaSsoServerProcessor;
import cn.dev33.satoken.sso.template.SaSsoUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.chen.base.convert.BeanConvert;
import com.chen.root.oclass.dto.UserInfoDTO;
import com.chen.root.base.R;
import com.chen.root.entity.AuthUser;
import com.chen.sa.auth.mapper.AuthUserMapper;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * Sa-Token-SSO Server端 Controller
 * @author ChenJinLong
 */
@RestController
@RequestMapping("/sso")
public class SsoController {

    @Resource
    private AuthUserMapper authUserMapper;

    @SaIgnore
    @PostMapping("/doLogin")
    @ApiOperation(value = "RestAPI 登录接口", tags = "RestAPI 登录接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "authType", value = "认证类型，如 password账号密码模式", required = true, dataType = "String"),
            @ApiImplicitParam(name = "paramMap", value = "认证参数，如 username 和 password", required = true, dataType = "String")
    })
    public R<String> doLogin(){
        Object loginObj = SaSsoServerProcessor.instance.ssoDoLogin();
        JSONObject loginJson = JSONObject.parseObject(JSONUtil.toJsonStr(loginObj));
        if (loginJson.getBoolean("success")){
            return R.ok(loginJson.getString("data"), loginJson.getString("msg"));
        }
        return R.failed(null, loginJson.getString("msg"));
    }

    @SaIgnore
    @PostMapping("/ssoAuth")
    @ApiOperation(value = "单点登录授权地址", tags = "单点登录授权地址")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "redirect", value = "登录成功后的重定向地址，一般填写 location.href（从哪来回哪去）", required = true, dataType = "String"),
            @ApiImplicitParam(name = "mode", value = "授权模式，取值 [simple, ticket]，simple=登录后直接重定向，ticket=带着ticket参数重定向，默认值为ticket", dataType = "String"),
            @ApiImplicitParam(name = "client", value = "客户端标识，不填代表是一个匿名应用，若填写了，则校验 ticket 时也必须是这个 client 才可以校验成功", dataType = "String")
    })
    public Object ssoAuth(){
        return SaSsoServerProcessor.instance.ssoAuth();
    }

    @SaIgnore
    @PostMapping("/checkTicket")
    @ApiOperation(value = "Ticket 校验接口", tags = "Ticket 校验接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "ticket", value = "ticket 参数", required = true, dataType = "String"),
            @ApiImplicitParam(name = "ssoLogoutCall", value = "单点注销时的回调通知地址，只在SSO模式三单点注销时需要携带此参数", dataType = "String"),
            @ApiImplicitParam(name = "client", value = "客户端标识，可不填，代表是一个匿名应用，若填写了，则必须填写的和 /sso/auth 登录时填写的一致才可以校验成功", dataType = "String"),
            @ApiImplicitParam(name = "timestamp", value = "当前时间戳，13位", required = true, dataType = "String"),
            @ApiImplicitParam(name = "nonce", value = "随机字符串", required = true, dataType = "String"),
            @ApiImplicitParam(name = "sign", value = "签名，生成算法：md5( [client={client值}&]nonce={随机字符串}&[ssoLogoutCall={单点注销回调地址}&]ticket={ticket值}&timestamp={13位时间戳}&key={secretkey秘钥} ) 注：[]内容代表可选", required = true, dataType = "String")
    })
    public Object checkTicket(){
        return SaSsoServerProcessor.instance.ssoCheckTicket();
    }

    @SaIgnore
    @PostMapping("/signout")
    @ApiOperation(value = "单点注销接口", tags = "单点注销接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "loginId", value = "要注销的账号 id", required = true, dataType = "String"),
            @ApiImplicitParam(name = "timestamp", value = "当前时间戳，13位", required = true, dataType = "String"),
            @ApiImplicitParam(name = "nonce", value = "随机字符串", required = true, dataType = "String"),
            @ApiImplicitParam(name = "sign", value = "签名，生成算法：md5( loginId={账号id}&nonce={随机字符串}&timestamp={13位时间戳}&key={secretkey秘钥} )", required = true, dataType = "String"),
            @ApiImplicitParam(name = "client", value = "客户端标识，可不填，一般在帮助 “sso-server 端不同client不同秘钥” 的场景下找到对应秘钥时，才填写", dataType = "String")
    })
    public Object logout(){
        return SaSsoServerProcessor.instance.ssoSignout();
    }

    @SaIgnore
    @PostMapping("/redirectUrl/ticket")
    @ApiOperation(value = "获取 redirectUrl 和 ticket", tags = "获取 redirectUrl 和 ticket")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "redirect", value = "登录成功后的重定向地址，一般填写 location.href（从哪来回哪去）", required = true, dataType = "String"),
            @ApiImplicitParam(name = "client", value = "客户端标识，不填代表是一个匿名应用; 若填写了，则校验 ticket 时也必须是这个 client 才可以校验成功", dataType = "String")
    })
    public R<String> getRedirectUrl(String redirect, String client) {
        // 未登录情况
        if(!StpUtil.isLogin()) {
            return R.failed(null, "未登录! ");
        }
        // 已登录情况下，构建 redirectUrl
        redirect = SaFoxUtil.decoderUrl(redirect);
        // 模式二或模式三
        String redirectUrl = SaSsoUtil.buildRedirectUrl(StpUtil.getLoginId(), client, redirect);
        return R.ok(redirectUrl);
    }

    @PostMapping("/getData")
    @ApiOperation(value = "获取user数据接口", tags = "获取user数据接口")
    public R<UserInfoDTO> getData(String apiType, String loginId) {
        // 校验签名：只有拥有正确秘钥发起的请求才能通过校验
        SaSignUtil.checkRequest(SaHolder.getRequest());
        AuthUser user = authUserMapper.selectById(Long.valueOf(loginId));
        if (user == null){
            return R.failed(null, "用户不存在! ");
        }
        return R.ok(BeanConvert.INSTANCE.convert(user));
    }
}
